All files / web/src/app/practice/[studentId]/review-timings page.tsx

0% Statements 0/50
0% Branches 0/1
0% Functions 0/1
0% Lines 0/50

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51                                                                                                     
import { notFound } from 'next/navigation'
import { canPerformAction } from '@/lib/classroom/access-control'
import { getPracticeStudent } from '@/lib/curriculum/server'
import { getUserId } from '@/lib/viewer'
import { ReviewTimingsClient } from './ReviewTimingsClient'

// Session/timing data must always be fresh (repairs self-correct at read time).
export const dynamic = 'force-dynamic'

interface ReviewTimingsPageProps {
  params: Promise<{ studentId: string }>
  searchParams: Promise<{ session?: string }>
}

/**
 * Timing Review Page — Server Component (#158).
 *
 * The single review destination (shared contract). Lists every flagged
 * (unusual/interrupted) timing across the student's recent sessions at the
 * player level, with per-attempt repair actions and a live estimate-recovery
 * header. Gated on `repair-data` (parent or teacher-present) to match the write
 * side — the whole page is a repair tool.
 *
 * URL: /practice/[studentId]/review-timings (optional ?session=<id> focus)
 */
export default async function ReviewTimingsPage({ params, searchParams }: ReviewTimingsPageProps) {
  const { studentId } = await params
  const { session } = await searchParams

  const player = await getPracticeStudent(studentId)
  if (!player) {
    notFound()
  }

  const viewerId = await getUserId()
  const canReview = await canPerformAction(viewerId, studentId, 'repair-data')
  if (!canReview) {
    // 404 rather than 403 to avoid leaking the player's existence.
    notFound()
  }

  return (
    <ReviewTimingsClient
      studentId={studentId}
      playerName={player.name}
      playerEmoji={player.emoji}
      focusSessionId={session}
    />
  )
}